﻿
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Drawing.Drawing2D;
using NSMyUtils;

/*
 This graphing code is Copyright (c) 2012 by Dan Gray, and Sidereal Technology.
 Feel free to modify this code for the project APMLogVisualizer.  If you do, please make
 code available to all users of APMLogVisualizer.
 This code not to be used in any other project without written permission from Dan Gray, 
 and Sidereal Technology.
*/

namespace EngineeringGraf
{
    internal class EngineeringGraph
    {
        private Random Ran;
        internal PointData[] PData;
        internal EngineeringGraph(System.Drawing.Graphics EngGraph, System.Windows.Forms.Form mom)
        {
            Ran = new Random(Environment.TickCount);
            g = EngGraph;
            Mom = mom;
            PData = new PointData[GConst.MaxLines];
            GridColor = System.Drawing.Color.FromArgb(22, 44, 22);
            InitializeData();
            InitializeGraph();
        }
        private System.Drawing.Graphics g;
        private System.Windows.Forms.Form Mom;
        internal Color VerticalScaleColor = Color.Goldenrod;
        internal Color HorizontalScaleColor = Color.Thistle;
        internal Color VerticalLabelColor = Color.HotPink;
        internal Color GraphBackColor = Color.Black;
        internal Color HorizontalLabelColor = Color.Aqua;
        internal Color TitleColor = Color.Chartreuse;
        internal Color GridColor;
        internal Color OutlineColor = Color.Blue;
        internal float CurWidthPoints = 2.0F;
        internal float CurWidthGrid = 2.0F;
        internal String TitleString = "This is the Graph Title";
        internal String VerticalLabel = "Vertical Data";
        internal String HorizontalLabel = "Horizontal Data";
        internal String HorizontalScale = "Horizontal Scale";
        internal String GFont = "Arial";

        //following are in inches, so the graph will scale with the window.
        const float GraphW = 8.0f;
        const float GraphH = 4.0f;
        private float VerticalLabelTextSize = 0.25F;
        private float HorizontalLabelTextSize = 0.25F;
        private float TitleStringSize = 0.3F;
        private float HorizontalScaleTextSize = 0.1F;
        private float VerticalScaleTextSize = 0.1F;
        private float PointTextSize = 0.25F;
        private float GraphWidth = GraphW;  //width of the graph area of the graph
        private float GraphHeight = GraphH; //height of the graph area of the graph
        private float SpaceLeft = 1;
        private float SpaceRight = 2.6f;
        private float SpaceBottom = 0.5F;
        private float SpaceTop = 0.5F;
        private float XScale, YScale;
        private float HorizontalGridLines = 6;
        private float VerticalGridLines = 10;

        //The following is used to zoom into the graph.
        internal float ZoomLeft = 0;
        internal float ZoomRight = GraphW;
        internal float ZoomTop = 0;
        internal float ZoomBot = GraphH;
        //***********************************
        internal void ResetZoom()
        {
            for(int i = 0; i < GConst.MaxLines; i++)
            {
                PData[i].ScreenStart = 0;
                PData[i].ScreenEnd = PData[i].DataLen;
                PData[i].ScreenSpan = PData[i].PointSpan;
                PData[i].ScreenZero = PData[i].PointZero;
                if (i == WhichPointUsedOnLeft)
                {
                    GraphBottomEU = PData[i].ScreenZero;
                    GraphTopEU = PData[i].ScreenSpan;
                    GraphLeftEU = PData[i].ScreenStart;
                    GraphRightEU = PData[i].ScreenEnd;
                }
            }
            Paint(g);
        }
        internal void FindZoomVars(int MouX1, int MouY1, int MouX2, int MouY2, int WhichLine)
        {
            int Strt, Fin;
            if (Math.Abs(MouX1 - MouX2) < 10) return;
            if (Math.Abs(MouY1 - MouY2) < 10) return;
            if (WhichLine < 0) { Strt = 0; Fin = GConst.MaxLines; }
            else { Strt = WhichLine; Fin = WhichLine + 1; }
            int i = Strt;
            if (MouX2 < MouX1) { int x = MouX1; MouX1 = MouX2; MouX2 = x; }
            if (MouY2 < MouY1) { int x = MouY1; MouY1 = MouY2; MouY2 = x; }
            for (; i < Fin; i++)
            {
                int Start = PData[i].ScreenStart;
                int End = PData[i].ScreenEnd;
                float Span = PData[i].ScreenSpan;
                float Zero = PData[i].ScreenZero;
                PData[i].ScreenStart = FindDataPointX(MouX1, Start, End);
                PData[i].ScreenEnd = FindDataPointX(MouX2, Start, End);
                PData[i].ScreenSpan = FindDataPointY(MouY1, Zero, Span);
                PData[i].ScreenZero = FindDataPointY(MouY2, Zero, Span);
                float s = Span; float z = Zero;
                Span = FindDataPointY(MouY1, z, s);
                Zero = FindDataPointY(MouY2, z, s);
                if (i == WhichPointUsedOnLeft)
                {
                    GraphTopEU = PData[i].ScreenSpan;
                    GraphBottomEU = PData[i].ScreenZero;
                    GraphLeftEU = PData[i].ScreenStart;
                    GraphRightEU = PData[i].ScreenEnd;
                }
            }
            Paint(g);
        }
        private float FindDataPointY(int MouY, float Zero, float Span)
        {
            float PercentY = MouY / YScale - SpaceTop;
            PercentY = PercentY / GraphHeight;
            if (PercentY > 1.0f) PercentY = 1.0f;
            if (PercentY < 0.0f) PercentY = 0.0f;
            PercentY = 1.0f - PercentY;
            //NowPercentX is 0-1, and represents where in the graph area, X the mouse is.
            float F = Span - Zero;
            F = F * PercentY;
            return F + Zero;
        }
        private int FindDataPointX(int MouX, int Start, int End)
        {
            float PercentX = MouX / XScale - SpaceLeft;
            PercentX = PercentX / GraphWidth;
            if (PercentX > 1.0f) PercentX = 1.0f;
            if (PercentX < 0.0f) PercentX = 0.0f;
            //NowPercentX is 0-1, and represents where in the graph area, X the mouse is.
            float F = End - Start;
            F = (float) Math.Round( F * PercentX);
            return (int) F + Start;
        }
        internal float Perc = 1.0f;
        //***********************************
        //***********************************
        //***********************************
        private void InitializeData()
        {
            System.Byte bright = 255; System.Byte dim = 64;
            System.Byte r = bright; System.Byte g = dim; System.Byte b = dim;
            for (int n = 0; n < GConst.MaxLines; n++)
            {
                PData[n] = new PointData();
                PData[n].PointName = "";
                PData[n].ScreenStart = 0;
                PData[n].ScreenEnd = PData[n].DataLen = GConst.MaxDataPoints - 1;
                PData[n].PointDescription = "";
                if (r == bright) { r = dim; g = bright; b = dim; }
                else if (g == bright) { r = dim; g = dim; b = bright; }
                else { r = bright; g = dim; b = dim; }
                PData[n].PointColor = System.Drawing.Color.FromArgb(r, g, b);
                PData[n].DecimalPoints = 3;
                PData[n].LineWidth = 1.5F;
                PData[n].PointSpan = 100.0F;
                PData[n].PointZero = 0.0F;
                PData[n].PointSpacingEU = 3.0;
                for (int a = 0; a < GConst.MaxDataPoints; a++)
                {
                    PData[n].DatArr[a].Value = 0;
                }
            }
        }
        //**********************************
        internal int NoOfLines = 0;
        internal int ThisDataLen = 0;
        internal bool AddLineToGraph(String Name, String Description, Color Col, float LineWidth, float Span, float Zero, double PointSpacingEU, float[] DataArray, int dp, int length)
        {
            LastXLine = LastMouX = LastXY  = -1;
            GraphTopEU = Span;
            GraphBottomEU = Zero;
            VerticalLabel = Name;
            VerticalLabelColor = Col;
            GraphLeftEU = 0.0f;
            GraphRightEU = length;
            if (NoOfLines >= GConst.MaxLines) NoOfLines = GConst.MaxLines - 1;
            PData[NoOfLines].ScreenStart = 0;
            PData[NoOfLines].ScreenEnd = PData[NoOfLines].DataLen = DataArray.Length;
            PData[NoOfLines].PointName = Name;
            PData[NoOfLines].PointDescription = Description;
            PData[NoOfLines].PointColor = Col;
            if (dp > -1) PData[NoOfLines].DecimalPoints = dp;
            PData[NoOfLines].LineWidth = LineWidth;
            PData[NoOfLines].PointSpan = Span;
            PData[NoOfLines].PointZero = Zero;
            PData[NoOfLines].ScreenSpan = Span;
            PData[NoOfLines].ScreenZero = Zero;
            PData[NoOfLines].PointSpacingEU = PointSpacingEU;
            int Len = DataArray.Length;
            if (Len > GConst.MaxDataPoints - 1) Len = GConst.MaxDataPoints - 1;
            if (Len > ThisDataLen) ThisDataLen = Len;
            int a = 0;
            for (a = 0; a < Len; a++)
            {
                PData[NoOfLines].DatArr[a].Value = DataArray[a];
            }
            for (; a < GConst.MaxDataPoints; a++)
            {
                PData[NoOfLines].DatArr[a].Value = 0;
            }
            WhichPointUsedOnLeft = NoOfLines;
            if (NoOfLines < GConst.MaxLines) NoOfLines++;
            InitializeGraph();
            ResetZoom();
            return true;
        }
        //**********************************
        //**********************************
        internal void RemoveLineFromGraph(String Name)
        {
            LastXLine = LastMouX = LastXY = -1;
            for (int i = 0; i < GConst.MaxLines; i++)
            {
                if (PData[i].PointName.IndexOf(Name) >= 0)
                {
                    if (NoOfLines > 0) NoOfLines--;
                    if (NoOfLines == 0)
                    {//if here, we deleted the last line.
                        ClearGraph();
                        return;
                    }
                    WhichPointUsedOnLeft = 0;
                    PData[i].PointName = "";
                    int i2 = i;
                    for (; i2 < GConst.MaxLines-1; i2++)
                    {
                        if (PData[i2+1].PointName.Length > 1)
                        {
                           CopyPData(i2, i2 + 1);
                        }
                        else break;
                    }
                    PData[i2].PointName = "";
                }
            }
            WhichPointUsedOnLeft = 0;
            GraphBottomEU = PData[WhichPointUsedOnLeft].ScreenZero;
            GraphTopEU = PData[WhichPointUsedOnLeft].ScreenSpan;
            VerticalLabel = PData[WhichPointUsedOnLeft].PointName;
            VerticalLabelColor = PData[WhichPointUsedOnLeft].PointColor;
            InitializeGraph();
        }
        //**********************************
        internal int LastXLine = -1;
        internal void DrawTickMark(Graphics g, int IndexIntoLog)
        {
            //Used to draw a mark where the current log pointer is.
            float x1 = 0, y1 = 0, y2 = 0;
            if (ThisDataLen < 1) return;
            Pen wpen = new Pen(Color.Green);
            float GraphDiffEU = GraphLeftEU - GraphRightEU;
            if (GraphDiffEU < 0.0F) GraphDiffEU = 0.0F - GraphDiffEU;
            float NoOfPoints = (float)GraphDiffEU; // ThisDataLen;
            float NoOfPixels = GraphWidth * XScale;

            if (NoOfPoints <= NoOfPixels)
            {// if here, there are more pixels than data point values. We draw lines
                NoOfPoints = (int)NoOfPoints;
                float PixelsPerPoint = NoOfPixels / NoOfPoints;
                x1 = ((SpaceLeft ) * XScale) + (((float)IndexIntoLog - GraphLeftEU) * PixelsPerPoint);
            }
            else
            {//If here, there are more data point values than pixels.  We draw pixels
                float xx = NoOfPixels / NoOfPoints;
                x1 = ((SpaceLeft ) * XScale) + (((float)IndexIntoLog - GraphLeftEU) * xx);
            }
            y1 = SpaceTop * YScale;
            y2 = (SpaceTop + GraphHeight) * YScale;
//            if(x1 < 0
            //x1 = (((GraphWidth / (VerticalGridLines)) * IndexIntoLog) + SpaceLeft) * XScale;
            if(LastXLine >= 0) MyUtils.GDI.DrawXORLine(g, wpen, LastXLine, (int)y1, LastXLine, (int)y2);  //erase
            MyUtils.GDI.DrawXORLine(g, wpen, (int)x1, (int)y1, (int)x1, (int)y2);
            LastXLine = (int) x1;
        }
        //**********************************
        internal void InitializeGraph()
        {
            g.Clear(GraphBackColor);
            XScale = g.VisibleClipBounds.Width / (GraphWidth + SpaceLeft + SpaceRight);
            YScale = g.VisibleClipBounds.Height / (GraphHeight + SpaceTop + SpaceBottom);
            DrawGraphName();
            DrawHorizontalGrid(); DrawVerticalGrid();
            DrawHorizontalScaleText();
            DrawHorizontalText();
            DrawGraphLines();
            DrawVerticalScaleText();
            DrawVerticalText();
            DrawPointNames();
            DrawOutline();
        }
        //**********************************
        //**********************************
        private void DrawPointNames()
        {
            float PST = PointTextSize * 0.5F * YScale;
            Font PSFont = new Font(GFont, PST);
            float X, Y;
            X = SpaceLeft + GraphWidth + 0.01F;
            SizeF stringSize = new SizeF();
            stringSize = g.MeasureString("HELLO", PSFont);
            SolidBrush eb = new SolidBrush(GraphBackColor);
            g.FillRectangle(eb, (SpaceLeft + GraphWidth) * XScale + 3, 1, (SpaceLeft + GraphWidth + SpaceRight) * XScale, GraphHeight * YScale);
            for (float n = 0; n < GConst.MaxLines; n++)
            {
                if (PData[(int)n].PointName.Length < 1) break;
                SolidBrush tb = new SolidBrush(PData[(int)n].PointColor);
                Y = SpaceTop + ((stringSize.Height / YScale + 0.01F) * n);
                g.DrawString(PData[(int)n].PointName + " Avg=" + Math.Round(PData[(int)n].ScreenAverage, PData[(int)n].DecimalPoints).ToString(), PSFont, tb, X * XScale, Y * YScale);
            }
        }
        internal void PrintString(String s, float x, float y)
        {
            Font PSFont = new Font(GFont, 15);
            SolidBrush eb = new SolidBrush(GraphBackColor);
            SolidBrush tb = new SolidBrush(Color.White);
            SizeF stringSize = new SizeF();
            stringSize = g.MeasureString(s, PSFont);
            g.FillRectangle(eb, x, y, x + (stringSize.Width * 1.2f), y + stringSize.Height);
            g.DrawString(s, PSFont, tb, x, y);

        }
        private void DrawPointNamesCurrent(int ingg)
        {
            float PST = PointTextSize * 0.5F * YScale;
            Font PSFont = new Font(GFont, PST);
            float X, Y;
            X = SpaceLeft + GraphWidth + 0.01F;
            SizeF stringSize = new SizeF();
            stringSize = g.MeasureString("HELLO", PSFont);
            SolidBrush eb = new SolidBrush(GraphBackColor);
            g.FillRectangle(eb, (SpaceLeft + GraphWidth) * XScale + 3, 1, (SpaceLeft + GraphWidth + SpaceRight) * XScale, GraphHeight * YScale);
            float LineWidth = stringSize.Height;
            for (float n = 0; n < GConst.MaxLines; n++)
            {
                if (PData[(int)n].PointName.Length < 1) break;
                SolidBrush tb = new SolidBrush(PData[(int)n].PointColor);
                Y = SpaceTop + ((stringSize.Height / YScale + 0.01F) * n);
                if (ingg > PData[(int)n].DatArr.Length - 1) ingg = PData[(int)n].DatArr.Length -1;
                if (ingg < 0) ingg = 0;

                String ns = Math.Round(PData[(int)n].DatArr[ingg].Value, PData[(int)n].DecimalPoints).ToString();
//The next two lines are specific for APMLogVisualizer                
                if (PData[(int)n].PointName.IndexOf("Mode") >= 0) ns = MyUtils.GetMode((int)PData[(int)n].DatArr[ingg].Value);
                else if (PData[(int)n].PointName.IndexOf("GPS_LT") >= 0) ns = MyUtils.MSecsSince0UTToLT((int)PData[(int)n].DatArr[ingg].Value);

                g.DrawString(PData[(int)n].PointName + "=" + ns, PSFont, tb, X * XScale, Y * YScale);
            }
        }
        //**********************************
        private void DrawGraphLines()
        {
            for (int n = 0; n < GConst.MaxLines; n++)
            {
                if (PData[n].PointName.Length < 1) break;
                if (n == WhichPointUsedOnLeft) continue;  //Candace's suggestion
                DrawGraphLine(PData[n]);
            }
            if (WhichPointUsedOnLeft >= GConst.MaxLines) return;
            if (PData[WhichPointUsedOnLeft].PointName.Length > 0) DrawGraphLine(PData[WhichPointUsedOnLeft]);  //Candace's Suggestion
        }
        //**********************************
        /*
          The variable PointSpacingEU is how many horizontal engineering
          units are between each point.  If there are more pixels than units
          between points, then the line segments will be at least longer than one pixel.
        */
        private void DrawGraphLine(PointData P)
        {
            float GraphDiffEU = GraphLeftEU - GraphRightEU;
            if (GraphDiffEU < 0.0F) GraphDiffEU = 0.0F - GraphDiffEU;
//            float NoOfPoints = (float)((double)GraphDiffEU / P.PointSpacingEU);
            float NoOfPoints = P.ScreenEnd - P.ScreenStart;
            float NoOfPixels = GraphWidth * XScale;
            Pen p = new Pen(P.PointColor, P.LineWidth);
            if (NoOfPoints <= NoOfPixels)
            {// if here, there are more pixels than data point values. We draw lines
                NoOfPoints = (int)NoOfPoints;
                PointF[] pntF = new PointF[(int)NoOfPoints];
                float PixelsPerPoint = NoOfPixels / NoOfPoints;
                for (int n = 0; n < NoOfPoints; n++)
                    pntF[n].X = ((SpaceLeft + GraphWidth) * XScale) - ((float)n * PixelsPerPoint);
                double Average = 0.0;
                for (int n = 0; n < NoOfPoints; n++)
                {
                    float Vert = P.DatArr[P.ScreenEnd - n].Value;
                    Average += Vert;
                    if (Vert > P.ScreenSpan) Vert = P.ScreenSpan; if (Vert < P.ScreenZero) Vert = P.ScreenZero;
                    Vert = (Vert / (P.ScreenSpan - P.ScreenZero)) - (P.ScreenZero / ((P.ScreenSpan - P.ScreenZero)));
                    Vert *= (GraphHeight * YScale);
                    pntF[n].Y = ((SpaceTop + GraphHeight) * YScale) - Vert;

                }
                Average /= (double) NoOfPoints;
                P.ScreenAverage = Average;
                try
                {
                    g.DrawLines(p, pntF);
                }
                catch
                {

                }
            }
            else
            {//If here, there are more data point values than pixels.  We draw pixels
                int Max = (int)(GraphWidth * XScale);
                PointF[] pntF = new PointF[Max];

                float PointPerPixels = NoOfPoints / NoOfPixels;
                double Average = 0.0;

                for (int n = 0; n < Max; n++)
                    pntF[n].X = ((SpaceLeft + GraphWidth) * XScale) - ((float)n);
                float ad = 0.0f;
                int n1 = 0;
                float Vert = 0.0f;
                for (n1 = 0; n1 < Max; n1++)
                {
                    ad = (float)n1 * PointPerPixels;
                    ad = (int)ad;
                    Vert = P.DatArr[P.ScreenEnd - (int)ad].Value;
                    Average += Vert;
                    if (Vert > P.ScreenSpan) Vert = P.ScreenSpan; if (Vert < P.ScreenZero) Vert = P.ScreenZero;
                    Vert = (Vert / (P.ScreenSpan - P.ScreenZero)) - (P.ScreenZero / ((P.ScreenSpan - P.ScreenZero)));
                    Vert *= (GraphHeight * YScale);
                    pntF[n1].Y = ((SpaceTop + GraphHeight) * YScale) - Vert;
                }
                Average /= (double)Max;
                P.ScreenAverage = Average;
                try
                {
                    g.DrawLines(p, pntF);
                }
                catch
                {
                }
            }
        }
        internal void ClearGraph()
        {
            WhichPointUsedOnLeft = 0;

            PData[0].PointName = "";
            PData[1].PointName = "";
            PData[2].PointName = "";
            PData[3].PointName = "";
            PData[4].PointName = "";
            PData[5].PointName = "";
            PData[6].PointName = "";
            PData[7].PointName = "";
            NoOfLines = 0;
            WhichPointUsedOnLeft = 0;
            ThisDataLen = 0;
            LastXLine = LastMouX = LastXY = -1;
            ResetZoom();
            InitializeGraph();
        }

        //***************************************************
        private void DrawHorizontalText()
        {// Must run DrawHorizontalScaleText() first
            float HST = HorizontalLabelTextSize * 0.5F * YScale;
            Font HSFont = new Font(GFont, HST);

            SolidBrush tb = new SolidBrush(HorizontalLabelColor);
            StringFormat drawFormat = new StringFormat();
            drawFormat.Alignment = StringAlignment.Center;
            float X = SpaceLeft + GraphWidth / 2.0F;
            float Y = SpaceTop + GraphHeight + 0.01F;
            g.DrawString(HorizontalLabel, HSFont, tb, X * XScale, Y * YScale +
                TallestHorizontalScaleText, drawFormat);
        }
        private void DrawHorizontalScaleText()
        {
            float HST = HorizontalScaleTextSize * 0.9F * YScale;
            Font HSFont = new Font(GFont, HST);
            SizeF stringSize = new SizeF();
            SolidBrush tb = new SolidBrush(HorizontalScaleColor);
            float X, Y;
            float EUStep = (GraphLeftEU - GraphRightEU) / VerticalGridLines;
            stringSize = g.MeasureString("EUS", HSFont);
            TallestHorizontalScaleText = stringSize.Height;
            for (float n = 0; n <= VerticalGridLines; n++)
            {
                float EU = GraphLeftEU - EUStep * (float)n;
                EU = (float)(Math.Round((double)EU, EUHorzDecPoints));
                String EUS = EU.ToString();

                StringFormat drawFormat = new StringFormat();
                drawFormat.Alignment = StringAlignment.Center;
                X = (GraphWidth / VerticalGridLines) * n + SpaceLeft;
                Y = GraphHeight + SpaceTop + 0.08F;
                g.DrawString(EUS, HSFont, tb, X * XScale, Y * YScale, drawFormat);
            }
        }
        //*****************
        private void DrawVerticalText()
        {//Must call DrawVerticalScaleText first, to update LongestVerticalScaleText
            float VST = VerticalLabelTextSize * 0.5F * XScale;
            Font VSFont = new Font(GFont, VST);
            SizeF stringSize = new SizeF();
            stringSize = g.MeasureString(VerticalLabel, VSFont);
            SolidBrush tb = new SolidBrush(VerticalLabelColor);
            StringFormat drawFormat = new StringFormat();
            drawFormat.FormatFlags = StringFormatFlags.DirectionVertical;
            drawFormat.Alignment = StringAlignment.Center;
            float X, Y;
            X = SpaceLeft - (stringSize.Height / XScale); Y = GraphHeight / 2 + SpaceTop;
            g.DrawString(VerticalLabel, VSFont, tb, X * XScale - LongestVerticalScaleText,
                Y * YScale, drawFormat);
        }
        //*****************
        //*****************
        private void DrawVerticalScaleText()
        {
            float VST = VerticalScaleTextSize * 0.9F * YScale;
            Font VSFont = new Font(GFont, VST);
            SizeF stringSize = new SizeF();
            SolidBrush tb = new SolidBrush(VerticalScaleColor);
            float X, Y;
            float EUStep = (GraphTopEU - GraphBottomEU) / HorizontalGridLines;
            //float EUStep = (PData[0].ScreenSpan - PData[0].ScreenZero) / HorizontalGridLines;
            LongestVerticalScaleText = 0;
            for (float n = 0; n <= HorizontalGridLines; n++)
            {
                float EU = GraphBottomEU + EUStep * (float)n;
                EU = (float)(Math.Round((double)EU, EUVertDecPoints));
                String EUS = EU.ToString();
                stringSize = g.MeasureString(EUS, VSFont);
                if (LongestVerticalScaleText < stringSize.Width) LongestVerticalScaleText = stringSize.Width;
                X = ((SpaceLeft - 0.0F) * XScale) - stringSize.Width;
                Y = (GraphHeight / HorizontalGridLines) * (HorizontalGridLines - n);
                Y = (SpaceTop + Y - 0.08F) * YScale;
                g.DrawString(EUS, VSFont, tb, X, Y);
            }
        }
        //*****************
        float LongestVerticalScaleText = 22.0F;
        float TallestHorizontalScaleText = 22.0F;
        //Vertical Scale Variables:
        //VerticalScaleTextSize
        internal float GraphLeftEU = 100.0F;
        internal float GraphRightEU = 0.0F;
        internal float GraphTopEU = 100.0F;
        internal float GraphBottomEU = 0.0F;   //used in auto verticle scaling
        internal int EUVertDecPoints = 2;
        internal int EUHorzDecPoints = 0;
        internal double HighestDataPoint = 100.0;
        internal int WhichPointUsedOnLeft = 0;
        private void DrawHorizontalGrid()
        {
            Pen p = new Pen(GridColor, CurWidthGrid);
            float X1 = (SpaceLeft) * XScale;
            float X2 = (SpaceLeft + GraphWidth) * XScale;
            for (float n = HorizontalGridLines - 1; n > 0; n--)
            {
                float Y1 = (((GraphHeight / (HorizontalGridLines)) * n) + SpaceTop) * YScale;
                float Y2 = Y1;
                g.DrawLine(p, X1, Y1, X2, Y2);
            }
        }

        private void DrawVerticalGrid()
        {
            Pen p = new Pen(GridColor, CurWidthGrid);
            float Y1 = SpaceTop * YScale;
            float Y2 = (SpaceTop + GraphHeight) * YScale;

            for (float n = 1; n < VerticalGridLines; n++)
            {
                float X1 = (((GraphWidth / (VerticalGridLines)) * n) + SpaceLeft) * XScale;
                g.DrawLine(p, X1, Y1, X1, Y2);
            }
        }
        //***********************************
        int TitleLenX = 0;
        int TitleLenY = 0;
        int TitleX = 0;
        int TitleY = 0;
        private void DrawGraphName()
        {
            float FS = TitleStringSize * 0.6F * YScale;
            Font TitleFont = new Font(GFont, FS);
            SizeF stringSize = new SizeF();
            stringSize = g.MeasureString(TitleString, TitleFont);
            SolidBrush tb = new SolidBrush(TitleColor);
            float X, Y;
            X = stringSize.Width / XScale;
            X = ((GraphWidth - X) / 2 + SpaceLeft) * XScale;
            Y = stringSize.Height / YScale;
            Y = (SpaceTop - Y - 0.01F) * YScale;
            g.DrawString(TitleString, TitleFont, tb, X, Y);
            TitleX = (int) X;
            TitleY = (int) Y;
            TitleLenX = (int)stringSize.Width;
            TitleLenY = (int)stringSize.Height;
        }
        private void DrawOutline()
        {
            Pen p = new Pen(OutlineColor, CurWidthPoints);
            g.DrawRectangle(p, SpaceLeft * XScale - 2, SpaceTop * YScale - 2, GraphWidth * XScale + 4, GraphHeight * YScale + 4);
        }
        internal void Paint(System.Drawing.Graphics e)
        {
            g = e;
            int lxl = LastXLine;
            LastXLine = LastMouX = LastXY = -1;
            //            DrawTickMark(g, lxl);
            InitializeGraph();
        }
        //*****************
        int MouDownX = 0; int MouDownY = 0;
        internal void MouseDown(MouseEventArgs e)
        {
            MouDownX = e.X; MouDownY = e.Y;
        }

        //*****************
        internal void MouseUp(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                FindZoomVars(MouDownX, MouDownY, e.X, e.Y, -1);
                //                PrintString((ClickOnDataLabel(e.X, e.Y)).ToString(),1,20);
                int w = ClickOnDataLabel(e.X, e.Y);
                if (w >= 0)
                {
                    if( w < GConst.MaxLines) WhichPointUsedOnLeft = w;
                    GraphBottomEU = PData[WhichPointUsedOnLeft].ScreenZero;
                    GraphTopEU = PData[WhichPointUsedOnLeft].ScreenSpan;
                    VerticalLabel = PData[WhichPointUsedOnLeft].PointName;
                    VerticalLabelColor = PData[WhichPointUsedOnLeft].PointColor;
                    Paint(g);
                }
                else
                {
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                if (!MouInGraphArea(e))
                {
                    int w = ClickOnDataLabel(e.X, e.Y);
                    if (w >= 0)
                    {
                        RemoveLineFromGraph(PData[w].PointName);
                        Paint(g);
                    }
                }
                else
                {
                   ResetZoom();
                }
            }
        }
        //*****************
        private bool MouInGraphArea(MouseEventArgs e)
        {
            float xy = e.X / XScale;
            if (xy < SpaceLeft) return false;
            if (xy > SpaceLeft + GraphWidth) return false;
            xy = e.Y / YScale;
            if (xy < SpaceTop) return false;
            if (xy > SpaceTop + GraphHeight) return false;
            return true;
        }
        //*****************
        //**********************************
        internal int ClickOnDataLabel(int MouX, int MouY)
        {
            MouX = MouX - (int) ((GraphWidth + SpaceLeft) * XScale) + 2;
            if (MouX < 2) return -1;
            float PST = PointTextSize * 0.5F * YScale;
            Font PSFont = new Font(GFont, PST);
            float Y;
            SizeF stringSize = new SizeF();
            stringSize = g.MeasureString("HELLO", PSFont);
            for (float n = 0; n < GConst.MaxLines; n++)
            {
                if (PData[(int)n].PointName.Length < 1) break;
                Y = (SpaceTop + ((stringSize.Height / YScale + 0.01F) * n)) * YScale;
        // PrintString("Y=" + Y.ToString() + " " + "Dif=" + (Y - MouY).ToString(), 1,  20 * n);
                if (MouY < Y + stringSize.Height && MouY > Y) return (int) n;
            }
            return -1;
        }
        internal bool ClickOnGraphTitle(int X, int Y)
        {
            if (X < TitleX) return false;
            if (X > TitleX + TitleLenX) return false;
            if (Y < TitleY) return false;
            if (Y > TitleY + TitleLenY) return false;
            return true;
        }
        //**********************************
        int LastXY = -1;
        int LastMouX = -1;
        int LastXOBX1 = -1;
        int LastXOBX2 = -1;
        int LastXOBY1 = -1;
        int LastXOBY2 = -1;
        Pen XORBoxPen = new Pen(Color.Green);
        internal int MouseMove(Graphics g, MouseEventArgs e)
        {
//            PrintString(e.Button.ToString() + " | " + MouseButtons.Left.ToString(), 1, 20);
            if (e.Button == MouseButtons.Left)
            {
//                PrintString(LastXOBX1.ToString() + " | " + LastXOBY1.ToString() + " | " + LastXOBX2.ToString() + " | " + LastXOBY2.ToString(), 1, 20);
                if (MouInGraphArea(e))
                {
                    if (e.X + e.Y != LastXY)
                    {
                        //if here, the mouse left is down, and it's moving, so must be a zoom thing.  We draw an xor box.
                        if (LastXY > 0)
                        {
                            //first we erase last box
                            Rectangle r = new Rectangle(LastXOBX1, LastXOBY1, LastXOBX2, LastXOBY2);
                            MyUtils.GDI.DrawXORRectangle(g, XORBoxPen,r);
                        }
                        //Now we draw the box
                        Rectangle r2 = new Rectangle(MouDownX, MouDownY, e.X - MouDownX, e.Y - MouDownY);
                        MyUtils.GDI.DrawXORRectangle(g, XORBoxPen, r2);
                        LastXOBX1 = MouDownX; LastXOBY1 = MouDownY; LastXOBX2 = e.X - MouDownX; LastXOBY2 = e.Y - MouDownY;
                    }
                    LastXY = e.X + e.Y;
                }
                return 0;
            }
            //Used to draw a Verticle Line where the current Mouse pointer is.
            //It returns the data point that the mouse is currently pointed to.
            float x1 = 0, y1 = 0, y2 = 0;
            if (ThisDataLen < 1) return -1;
            Pen wpen = new Pen(Color.Blue);
            float GraphDiffEU = GraphLeftEU - GraphRightEU;
            if (GraphDiffEU < 0.0F) GraphDiffEU = 0.0F - GraphDiffEU;
            float NoOfPoints = (float)GraphDiffEU; // ThisDataLen;
            float NoOfPixels = GraphWidth * XScale;

            if (NoOfPoints <= NoOfPixels)
            {// if here, there are more pixels than data point values.
                NoOfPoints = (int)NoOfPoints;
                float PixelsPerPoint = NoOfPixels / NoOfPoints;
                x1 = ((SpaceLeft) * XScale) + (((float)e.X - GraphLeftEU) * PixelsPerPoint);
            }
            else
            {//If here, there are more data point values than pixels.
                float xx = NoOfPixels / NoOfPoints;
                x1 = ((SpaceLeft) * XScale) + (((float)e.X - GraphLeftEU) * xx);
            }

            float IntoArray = e.X - ((SpaceLeft) * XScale);
            if (IntoArray < 0) IntoArray = 0;
            IntoArray = IntoArray / (GraphWidth * XScale);
            IntoArray = ((PData[WhichPointUsedOnLeft].ScreenEnd - PData[WhichPointUsedOnLeft].ScreenStart) * IntoArray) + PData[WhichPointUsedOnLeft].ScreenStart;
            IntoArray = (float)Math.Round(IntoArray);
            //            PrintString(IntoArray.ToString(), 1, 20);

            y1 = SpaceTop * YScale;
            y2 = (SpaceTop + GraphHeight) * YScale;
            x1 = e.X;
            if ((float)x1 / XScale < SpaceLeft)
            {
                x1 = SpaceLeft * XScale - 1;
                DrawPointNames();
            }
            else if ((float)x1 / XScale > (SpaceLeft + GraphWidth))
            {
                x1 = ((SpaceLeft + GraphWidth) * XScale) + 2;
                DrawPointNames();
            }
            else
            {
                DrawPointNamesCurrent((int)IntoArray);
                MouseIntoArray = (int) IntoArray;
            }
            // PrintString(IntoArray.ToString(), 1, 12);
            if (LastMouX >= 0) MyUtils.GDI.DrawXORLine(g, wpen, LastMouX, (int)y1, LastMouX, (int)y2);  //erase
            MyUtils.GDI.DrawXORLine(g, wpen, (int)x1, (int)y1, (int)x1, (int)y2);
            LastMouX = (int)x1;
            return 0;
        }
        internal int MouseIntoArray = 0;
        private void CopyPData(int dest, int src)
        {
            PData[dest].ScreenStart = PData[src].ScreenStart;
            PData[dest].ScreenEnd = PData[src].ScreenEnd;
            PData[dest].ScreenSpan = PData[src].ScreenSpan;
            PData[dest].ScreenZero = PData[src].ScreenZero;
            PData[dest].DataLen = PData[src].DataLen;
            PData[dest].DecimalPoints = PData[src].DecimalPoints;
            PData[dest].PointColor = PData[src].PointColor;
            PData[dest].LineWidth = PData[src].LineWidth;
            PData[dest].PointName = PData[src].PointName;
            PData[dest].PointDescription = PData[src].PointDescription;
            PData[dest].PointSpan = PData[src].PointSpan;
            PData[dest].PointZero = PData[src].PointZero;
            PData[dest].ScreenAverage= PData[src].ScreenAverage;
            PData[dest].PointSpacingEU = PData[src].PointSpacingEU;
            for (int i = 0; i < GConst.MaxDataPoints; i++)
            {
                PData[dest].DatArr[i].Value = PData[src].DatArr[i].Value;
            }
        }
    }
    //******************************************
    internal class PointData
    {
        //following four variables are for zooming into the graph.
        internal int ScreenStart;
        internal int ScreenEnd;
        internal float ScreenSpan;
        internal float ScreenZero;

        internal int DataLen;
        internal int DecimalPoints;
        internal Color PointColor;
        internal float LineWidth;
        internal String PointName;
        internal String PointDescription;
        internal float PointSpan;
        internal float PointZero;
        internal double ScreenAverage;
        internal double PointSpacingEU; //separation between points in time, deg's, or whatever
        internal DataPoint[] DatArr = new DataPoint[GConst.MaxDataPoints];
        internal PointData()
        {
        }
        internal struct DataPoint
        {
            internal float Value;
            //			internal long TimeStamp;
        }
    }
    //******************************************
    internal class GConst
    {
        internal static int MaxLines = 8;
        internal static double pi = 3.1415926;
        internal static int MaxDataPoints = 65536;
    }
}

